<?php
/*
 * IP类
 * @author andery <andery@foxmail.com>
 */
class CIp 
{
    /**
     * 获得客户端真实IP地址
     * @return string
     */
    public static function get_real_ip()
    {
        if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } elseif (!empty($_SERVER['REMOTE_ADDR'])) {
            $ip = $_SERVER['REMOTE_ADDR'];
        } else {
            $ip = 'Unknown';
        }
        return trim($ip);
    }
    
    /**
     * 获得IP所在城市
     * 
     * 城市数据格式 '返回标识'=>'城市名称'
     */
    public static function get_ip_city($dat_path, $ip, $citys=array())
    {
        if ($citys) {
            $place = self::get_ip_place($dat_path, $ip);
            foreach ($citys as $key=>$val) {
                if (strpos($place, $val)) {
                    return $key;
                }
            }
        }
        return false;
    }
    
    /**
     * 获得IP所在详细地址
     */
    public static function get_ip_place($dat_path, $ip)
    {
        //检查IP地址
        if (!preg_match("/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/", $ip)) {
            return '-2';
        }
        //打开IP数据文件
        if (!$fd = @fopen($dat_path, 'rb')) {
            return '-1';
        }
        $ip = explode('.', $ip);
        $ip_num=($ip[0] * 256*256*256) + ($ip[1]*256*256) + ($ip[2]*256) + $ip[3];
        
        //获取IP数据索引开始和结束位置
        $DataBegin = fread($fd, 4);
        $DataEnd = fread($fd, 4);
        $ipbegin = implode('', unpack('L', $DataBegin));
        if ($ipbegin < 0) {
            $ipbegin += pow(2, 32);
        }
        $ipend = implode('', unpack('L', $DataEnd));
        if ($ipend < 0) {
        	$ipend += pow(2, 32);
        }
        $ipAllNum = ($ipend - $ipbegin) / 7 + 1;
    
        $BeginNum = 0;
        $EndNum = $ipAllNum;
        $ip1num = $ip2num = 0;
        
        while ($ip1num>$ip_num || $ip2num<$ip_num) {
            $Middle= intval(($EndNum + $BeginNum) / 2);
            fseek($fd, $ipbegin + 7 * $Middle);
            $ipData1 = fread($fd, 4);
            if(strlen($ipData1) < 4) {
                fclose($fd);
                return '0';
            }
            $ip1num = implode('', unpack('L', $ipData1));
            if ($ip1num < 0) {
            	$ip1num += pow(2, 32);
            }
        	if($ip1num > $ip_num) {
            	$EndNum = $Middle;
            	continue;
        	}
        	
        	$DataSeek = fread($fd, 3);
        	if (strlen($DataSeek) < 3) {
            	fclose($fd);
            	return '0';
        	}
        	$DataSeek = implode('', unpack('L', $DataSeek.chr(0)));
        	fseek($fd, $DataSeek);
        	$ipData2 = fread($fd, 4);
        	if(strlen($ipData2) < 4) {
            	fclose($fd);
            	return '0';
        	}
        	$ip2num = implode('', unpack('L', $ipData2));
        	if ($ip2num < 0) {
        		$ip2num += pow(2, 32);
        	}
        	if($ip2num < $ip_num) {
            	if($Middle == $BeginNum) {
                	fclose($fd);
                	return 'Unknown';
            	}
            	$BeginNum = $Middle;
        	}
    	}
    	$ipAddr1 = $ipAddr2 = '';
    	$ipFlag = fread($fd, 1);
    	if ($ipFlag == chr(1)) {
        	$ipSeek = fread($fd, 3);
        	if (strlen($ipSeek) < 3) {
            	fclose($fd);
            	return '0';
        	}
        	$ipSeek = implode('', unpack('L', $ipSeek.chr(0)));
        	fseek($fd, $ipSeek);
        	$ipFlag = fread($fd, 1);
    	}

    	if ($ipFlag == chr(2)) {
        	$AddrSeek = fread($fd, 3);
        	if(strlen($AddrSeek) < 3) {
            	fclose($fd);
            	return '0';
        	}
        	$ipFlag = fread($fd, 1);
        	if($ipFlag == chr(2)) {
            	$AddrSeek2 = fread($fd, 3);
            	if(strlen($AddrSeek2) < 3) {
                    fclose($fd);
                    return '0';
                }
                $AddrSeek2 = implode('', unpack('L', $AddrSeek2.chr(0)));
                fseek($fd, $AddrSeek2);
        	} else {
                fseek($fd, -1, SEEK_CUR);
        	}
        	while (($char = fread($fd, 1)) != chr(0)) {
        	    $ipAddr2 .= $char;
        	}

            $AddrSeek = implode('', unpack('L', $AddrSeek.chr(0)));
            fseek($fd, $AddrSeek);

            while (($char = fread($fd, 1)) != chr(0)) {
                $ipAddr1 .= $char;
            }
        } else {
            fseek($fd, -1, SEEK_CUR);
            while (($char = fread($fd, 1)) != chr(0)) {
            	$ipAddr1 .= $char;
            }

            $ipFlag = fread($fd, 1);
            if($ipFlag == chr(2)) {
                $AddrSeek2 = fread($fd, 3);
                if(strlen($AddrSeek2) < 3) {
                    fclose($fd);
                    return '0';
                }
                $AddrSeek2 = implode('', unpack('L', $AddrSeek2.chr(0)));
                fseek($fd, $AddrSeek2);
            } else {
                fseek($fd, -1, SEEK_CUR);
            }
            while(($char = fread($fd, 1)) != chr(0)){
                $ipAddr2 .= $char;
            }
        }
        fclose($fd);
        if(preg_match('/http/i', $ipAddr2)) {
            $ipAddr2 = '';
        }
        $ipaddr = "$ipAddr1 $ipAddr2";
        $ipaddr = preg_replace('/CZ88.Net/is', '', $ipaddr);
        $ipaddr = preg_replace('/^s*/is', '', $ipaddr);
        $ipaddr = preg_replace('/s*$/is', '', $ipaddr);
        if(preg_match('/http/i', $ipaddr) || $ipaddr == '') {
            $ipaddr = 'Unknown';
        }
        return iconv("gbk","utf-8",trim($ipaddr));
    }
}